package away3d.cameras.lenses {
	import away3d.arcane;
	import away3d.containers.*;
	import away3d.core.base.*;
	import away3d.core.geom.*;
	import away3d.core.math.Vector3DUtils;

	import flash.geom.*;
	
	use namespace arcane;
	
	public class ShadowBoxLens extends AbstractLens
	{
		private var _offset:Number = 1;
		public var r:Number = 1160;
		public var l:Number = 1;
		public var t:Number = 1160;
		public var b:Number = 1;
		public var n:Number = 1000;
		public var f:Number = Infinity;
		
		public var headX:Number = 1;  //represent inches()
		public var headY:Number = 1;  //represent inches()
		public var headDist:Number = 36;
		public var dpi:Number = 72;
		public var headAngle:Number = 0;
		
		private var _hd:Number = 1;
		private var _hoX:Number = 1;
		private var _hoY:Number = 1;
		/** @private */
		arcane override function setView(val:View3D):void
		{
			super.setView(val);
			
			if (_clipping.minZ == -Infinity)
        		_near = -_camera.focus/2;
        	else
        		_near = _clipping.minZ;

			//trace("focus is " + _camera.focus);
			//_projectionMatrix.rawData = Vector.<Number>([_camera.zoom*_camera.focus, 0, 0, 0, 0, _camera.zoom*_camera.focus, 0, 0, _offset*0.001, 0, 1, 1, 0, 0, _camera.focus, _camera.focus]);
			_projectionMatrix.rawData = Vector.<Number>([_camera.zoom*_camera.focus, 0, 0, 0, 0, _camera.zoom*_camera.focus, 0, 0, 0, 0, 1, 1, 0, 0, _camera.focus, _camera.focus]);
			
			
		}
		/** @private */
		arcane override function getFrustum(node:Object3D, viewTransform:Matrix3D):Frustum
		{

			_frustum = _cameraVarsStore.createFrustum(node);
			_focusOverZoom = _camera.focus/_camera.zoom;
			_zoom2 = _camera.zoom*_camera.zoom;
			
			_plane = _frustum.planes[Frustum.NEAR];
			_plane.a = 0;
			_plane.b = 0;
			_plane.c = 1;
			_plane.d = -_near;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.FAR];
			_plane.a = 0;
			_plane.b = 0;
			_plane.c = -1;
			_plane.d = _far;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.LEFT];
			_plane.a = _clipHeight*_focusOverZoom;
			_plane.b = 0;
			//_plane.c = -_clipHeight*_clipLeft/_zoom2;
			_plane.c = -1000;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.RIGHT];
			_plane.a = -_clipHeight*_focusOverZoom;
			_plane.b = 0;
			_plane.c = (_clipHeight*_clipRight/_zoom2);
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.TOP];
			_plane.a = 0;
			_plane.b = _clipWidth*_focusOverZoom;
			_plane.c = -_clipWidth*_clipTop/_zoom2;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
			
			_plane = _frustum.planes[Frustum.BOTTOM];
			_plane.a = 0;
			_plane.b = -_clipWidth*_focusOverZoom;
			_plane.c = _clipWidth*_clipBottom/_zoom2;
			_plane.d = _plane.c*_camera.focus;
			_plane.transform(viewTransform);
						
			return _frustum;
		}
		/** @private */
		arcane override function getFOV():Number
		{
			//calculated from the arctan addition formula arctan(x) + arctan(y) = arctan(x + y / 1 - x*y)
			return Math.atan2(_clipHeight, _camera.focus*_camera.zoom + _clipTop*_clipBottom/(_camera.focus*_camera.zoom))*toDEGREES;
		}
		/** @private */
		arcane override function getZoom():Number
		{
			var b:Number = _clipHeight/Math.tan(_camera.fov*toRADIANS);
			return (b + Math.sqrt(Math.pow(b, 2) - 4*_clipTop*_clipBottom/_camera.zoom))/(2*_camera.focus);
		}
		/** @private */
		arcane override function getT(screenZ:Number):Number
		{
			return 1/(_camera.focus + screenZ);
		}
		/** @private */
		arcane override function getScreenZ(t:Number):Number
		{
			return 1/t - _camera.focus;
		}
		/** @private */
		arcane override function project(viewTransform:Matrix3D, verts:Vector.<Number>, screenVerts:Vector.<Number>, uvts:Vector.<Number>):void
		{
			
   			var unityMatrix:Matrix3D =  new Matrix3D();
   			unityMatrix.rawData = Vector.<Number>([1, 0, 0, 0,    0, 1, 0, 0,   0, 0, 1, 0,   0, 0, 0, 1]);
			
			var notRotationMatrix:Matrix3D = stripOutRotation(viewTransform);
			
			var newVerts:Vector.<Number> = new Vector.<Number>();
			notRotationMatrix.transformVectors(verts, newVerts);
			
			
			
			var obliqueVerts:Vector.<Number> = projectObliquePerspective(newVerts, viewTransform);
			
        	Utils3D.projectVectors(unityMatrix, obliqueVerts, screenVerts, uvts);

        }
		
        /**
         * @inheritDoc
         */
		public override function getPerspective(screenZ:Number):Number
		{
			return _camera.zoom / (1 + screenZ / _camera.focus);
		}
		
		public function upateFustrum(xVal:Number, yVal:Number):void {	
			

			
		}
		
		public function scewProjection(m:Matrix3D, verts:Vector.<Number>, projectedVerts:Vector.<Number>, uvts:Vector.<Number>):void
		{
			
			trace("scewProjection");
			trace("m " + m);
			trace("verts "+verts);
			trace("projectedVerts " + projectedVerts);
			trace("uvts " + uvts);
		
		}
		
		public function updatePerspective(hd:Number, hoX:Number, hoY:Number):void {	
			
			_hd = hd;
			_hoX = 0 - hoX;
			_hoY = 0 - hoY;
			trace("ho " + _hoX);
			
		}
		
		public function projectObliquePerspective(worldVerts:Vector.<Number>, objectTransform:Matrix3D):Vector.<Number>
		{
			
			var projections:Vector.<Number> = new Vector.<Number>();
			var points:Array =  new Array();
			var count:int = 0;
			var vertsLength:Number = worldVerts.length;
			
			for (var i:int = 0; i <  vertsLength; i ++ )
			{
				count ++;
				var calc:Number = 0;
				trace("COUNT AT " + count + " i at " + i);
				// set x
				if (count  == 1){
					calc = (  (_hd / (_hd - worldVerts[i+2] + objectTransform.position.z))   * (worldVerts[i] + objectTransform.position.x + _hoX)   )  - _hoX;
					projections.push(calc);
				//set y
				} else if (count == 2) {
					calc = (  (_hd / (_hd - worldVerts[i+1] + objectTransform.position.z))   * (worldVerts[i] + objectTransform.position.y + _hoY)   )  - _hoY;
					projections.push(calc);
				//set z
				}else {
					projections.push( worldVerts[i] + objectTransform.position.z);
					count = 0;
				}
				
			}
		
			return projections;

		}
		
		public function updateUVTS(uvts:Vector.<Number>):Vector.<Number>
		{
			var projections:Vector.<Number> = new Vector.<Number>();
			var points:Array =  new Array();
			var count:int = 0;
			var vertsLength:Number = uvts.length;
			
			for (var i:int = 0; i <  vertsLength; i ++ )
			{
				count ++;
				var calc:Number = 0;
				if (count  == 1){
					
					projections.push(0);
				} else if (count == 2) {
					
					projections.push  (0);
				}else {
					
					calc = (  1 / ( _hd + uvts[i] )  );
					projections.push( calc * 2);
					count = 0;
				}
				
			}
		
			return projections;	
		}
		
		public function returnAnObliqueMatrix():Matrix3D
		{
   			//var newMatrix:Matrix3D =  new Matrix3D();
   			//newMatrix.rawData = Vector.<Number>([1, 0, 0, 0,    0, 1, 0, 0,   0, 0, 1, 0,   0, 0, 0, 1]);
			trace("_hd" + _hd);
			var newMatrix:Matrix3D = new Matrix3D();
			newMatrix.rawData = Vector.<Number>([_hd, 0, 0, 0,      0, _hd, 0, 0,      _hoX, _hoY, 1, -1,      2 * _hd * _hoX, 2 * _hd * _hoY, 0, 0]);
			//newMatrix.rawData = Vector.<Number>([_hd, 0, _hoX, _hd*_hoX,    0, _hd, _hoY, _hd*+_hoY,   0, 0, 1, 0,  0,0, 1, 0]);
			
			return newMatrix;
		}
		
		/*
		 * return a matrix with no rotation in it
		 */
		public function stripOutTranslation(matrix:Matrix3D):Matrix3D
		{
			var returnMatrix:Matrix3D = new Matrix3D();
			if (matrix.rawData.length < 1){ return returnMatrix };
			var newArray:Vector.<Number>  = new Vector.<Number>();
			var maxIndex:int = matrix.rawData.length;
			for (var i:int = 0; i < maxIndex; i ++){
				
				if ( i == 15) 
				{
					newArray.push(1);
				} 
				else if (i > 11 && i < 15)
				{
					newArray.push(0);
				}
				else if (i == 5)
				{
					newArray.push(Number(0 - matrix.rawData[i]) );
				}
				else 
				{
					newArray.push(Number(matrix.rawData[i]));
				}
			}
			
			returnMatrix.rawData = newArray;
			
			return returnMatrix;
			
		}
		
		/*
		 * return a matrix with no rotation in it
		 */
		public function stripOutRotation(matrix:Matrix3D):Matrix3D
		{
			var returnMatrix:Matrix3D = new Matrix3D();
			if (matrix.rawData.length < 1){ return returnMatrix };
			var newArray:Vector.<Number>  = new Vector.<Number>();
			var maxIndex:int = matrix.rawData.length;
			for (var i:int = 0; i < maxIndex; i ++){
				
				if ( i == 15) 
				{
					newArray.push(1);
				} 
				else if (i > 11 && i < 15)
				{
					newArray.push(0);
				}
				else if (i == 5)
				{
					newArray.push(Number(0 - matrix.rawData[i]) );
				}
				else 
				{
					newArray.push(Number(matrix.rawData[i]));
				}
			}
			
			returnMatrix.rawData = newArray;
			
			return returnMatrix;
			
		}
					
	}
}